package org.yun.octopus.base.core;

import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.yun.octopus.base.enums.RoleType;
import org.yun.octopus.base.ex.ReverseException;
import org.yun.octopus.base.spi.IMarkCache;
import org.yun.octopus.base.spi.IMqService;
import org.yun.octopus.base.spi.IRollback;
import org.yun.octopus.base.util.ParamHelper;

import javax.annotation.Resource;
import java.lang.reflect.InvocationTargetException;
import java.util.List;

import static org.yun.octopus.base.config.Cfg.backAsync;
import static org.yun.octopus.base.enums.CallType.*;
import static org.yun.octopus.base.enums.ResType.FALLBACK_SUCCESS;
import static org.yun.octopus.base.enums.ResType.SUCCESS;

/**
 * @author liyunfeng31
 */
@Component
public class RollBackHandler implements IRollback {

    protected static final Logger LOG = LoggerFactory.getLogger(RollBackHandler.class);

    @Resource
    private IMqService mqService;

    @Resource
    private IMarkCache markCache;


    /**
     * 逆向处理类
     * @param group 业务组
     * @param bizNo 业务编号
     * @param mode  1-同步调用且部分step  2-异步消费MQ且部分step   3-(MQ/脚本补偿)同步调用全部step
     */
    @Override
    public void execute(String group, String bizNo, int mode) throws Exception{

        if(mode == SYNC_ALL.code()){
            reverse(group, bizNo, RollbackFactory.getSteps(group));
            LOG.info("SYNC_ALL MODE, rollback all steps, BizNo:{} ",bizNo);
            return;
        }

        List<Integer> part = markCache.stepNos(group, bizNo);

        if(backAsync && mode != MQ_PART.code()){
            LOG.info("BizNo:{} sendFallbackMsg, part_steps:{} ",bizNo, JSON.toJSONString(part));
            mqService.sendFallbackMsg(group, bizNo, part);
        }else{
            LOG.info("BizNo:{} sync fallback, part_steps:{} ",bizNo, JSON.toJSONString(part));
            reverse(group, bizNo, part);
        }
    }


    /**
     * 逆向执行
     * @param bizNo 业务编号
     * @param stepNos 逆向调用的步骤集合
     */
    private void reverse(String group, String bizNo, List<Integer> stepNos)
            throws IllegalAccessException,
            InvocationTargetException,
            ReverseException {

        for (Integer no : stepNos) {
            Rollback fallback = RollbackFactory.getFallback(no);

            if (fallback == null) {
                LOG.warn("stepNo: [{}] no fallback method", no);
                continue;
            }

            if(fallback.getRole() == RoleType.CHILD){
               bizNo = markCache.childBizNo(group, bizNo, no);
            }

            Object result = fallback.getMethod().invoke(fallback.getClz(), bizNo);
            if(result == null || ParamHelper.getResType(result).equals(SUCCESS)){
                LOG.info("BizNo:{} call fallback method success, stepNo: {}, resp: {} ",bizNo, no, JSON.toJSONString(result));
                markCache.save(group, bizNo, no, FALLBACK_SUCCESS.code(),null);
                continue;
            }
            throw new ReverseException("Reverse exception");
        }
        LOG.info("BizNo:{} fallback success finish, clear mark",bizNo);
        markCache.clearMark(group,bizNo);
    }
}
